module sim_mig(
  input          i_clk,
  input          i_rst,
  input  [ 29:2] i_app_addr,
  input  [  2:0] i_app_cmd,
  input          i_app_en,
  output         o_app_rdy,
  output [511:0] o_app_rd_data,
  output         o_app_rd_data_valid,
  input  [511:0] i_app_wdf_data,
  input          i_app_wdf_end,
  input  [ 63:0] i_app_wdf_mask,
  output         o_app_wdf_rdy,
  input          i_app_wdf_wren
);

reg [7:0] mem[2**20-1:0]; // 1MB

reg [511:0] rd_data;
reg         rd_data_valid;

reg [511:0] wr_data;
reg [ 63:0] wr_mask;
reg         wdf_rdy;

assign o_app_rdy = 1'b1;
assign o_app_rd_data = rd_data;
assign o_app_rd_data_valid = rd_data_valid;
assign o_app_wdf_rdy = wdf_rdy;

reg [639:0] dram_init_file;
integer fd;
integer dummy;

initial begin
  if ($value$plusargs("dram_init=%s", dram_init_file) != 0) begin
    fd = $fopen(dram_init_file, "rb");
    dummy = $fread(mem, fd);
  end
end

always @(posedge i_clk) begin
  if (i_rst) begin
    rd_data_valid <= 1'b0;
    wdf_rdy <= 1'b1;
  end else begin
    if (i_app_en) begin
      if (i_app_cmd[0]) begin
        // read
        rd_data <= {
          mem[{i_app_addr[19:6],6'd63}],
          mem[{i_app_addr[19:6],6'd62}],
          mem[{i_app_addr[19:6],6'd61}],
          mem[{i_app_addr[19:6],6'd60}],
          mem[{i_app_addr[19:6],6'd59}],
          mem[{i_app_addr[19:6],6'd58}],
          mem[{i_app_addr[19:6],6'd57}],
          mem[{i_app_addr[19:6],6'd56}],
          mem[{i_app_addr[19:6],6'd55}],
          mem[{i_app_addr[19:6],6'd54}],
          mem[{i_app_addr[19:6],6'd53}],
          mem[{i_app_addr[19:6],6'd52}],
          mem[{i_app_addr[19:6],6'd51}],
          mem[{i_app_addr[19:6],6'd50}],
          mem[{i_app_addr[19:6],6'd49}],
          mem[{i_app_addr[19:6],6'd48}],
          mem[{i_app_addr[19:6],6'd47}],
          mem[{i_app_addr[19:6],6'd46}],
          mem[{i_app_addr[19:6],6'd45}],
          mem[{i_app_addr[19:6],6'd44}],
          mem[{i_app_addr[19:6],6'd43}],
          mem[{i_app_addr[19:6],6'd42}],
          mem[{i_app_addr[19:6],6'd41}],
          mem[{i_app_addr[19:6],6'd40}],
          mem[{i_app_addr[19:6],6'd39}],
          mem[{i_app_addr[19:6],6'd38}],
          mem[{i_app_addr[19:6],6'd37}],
          mem[{i_app_addr[19:6],6'd36}],
          mem[{i_app_addr[19:6],6'd35}],
          mem[{i_app_addr[19:6],6'd34}],
          mem[{i_app_addr[19:6],6'd33}],
          mem[{i_app_addr[19:6],6'd32}],
          mem[{i_app_addr[19:6],6'd31}],
          mem[{i_app_addr[19:6],6'd30}],
          mem[{i_app_addr[19:6],6'd29}],
          mem[{i_app_addr[19:6],6'd28}],
          mem[{i_app_addr[19:6],6'd27}],
          mem[{i_app_addr[19:6],6'd26}],
          mem[{i_app_addr[19:6],6'd25}],
          mem[{i_app_addr[19:6],6'd24}],
          mem[{i_app_addr[19:6],6'd23}],
          mem[{i_app_addr[19:6],6'd22}],
          mem[{i_app_addr[19:6],6'd21}],
          mem[{i_app_addr[19:6],6'd20}],
          mem[{i_app_addr[19:6],6'd19}],
          mem[{i_app_addr[19:6],6'd18}],
          mem[{i_app_addr[19:6],6'd17}],
          mem[{i_app_addr[19:6],6'd16}],
          mem[{i_app_addr[19:6],6'd15}],
          mem[{i_app_addr[19:6],6'd14}],
          mem[{i_app_addr[19:6],6'd13}],
          mem[{i_app_addr[19:6],6'd12}],
          mem[{i_app_addr[19:6],6'd11}],
          mem[{i_app_addr[19:6],6'd10}],
          mem[{i_app_addr[19:6],6'd 9}],
          mem[{i_app_addr[19:6],6'd 8}],
          mem[{i_app_addr[19:6],6'd 7}],
          mem[{i_app_addr[19:6],6'd 6}],
          mem[{i_app_addr[19:6],6'd 5}],
          mem[{i_app_addr[19:6],6'd 4}],
          mem[{i_app_addr[19:6],6'd 3}],
          mem[{i_app_addr[19:6],6'd 2}],
          mem[{i_app_addr[19:6],6'd 1}],
          mem[{i_app_addr[19:6],6'd 0}]};
        rd_data_valid <= 1'b1;
      end else begin
        rd_data_valid <= 1'b0;
        // write
        if (~wdf_rdy) begin
          if (~wr_mask[ 0]) mem[{i_app_addr[19:6],6'd00}] <= wr_data[  0+:8];
          if (~wr_mask[ 1]) mem[{i_app_addr[19:6],6'd01}] <= wr_data[  8+:8];
          if (~wr_mask[ 2]) mem[{i_app_addr[19:6],6'd02}] <= wr_data[ 16+:8];
          if (~wr_mask[ 3]) mem[{i_app_addr[19:6],6'd03}] <= wr_data[ 24+:8];
          if (~wr_mask[ 4]) mem[{i_app_addr[19:6],6'd04}] <= wr_data[ 32+:8];
          if (~wr_mask[ 5]) mem[{i_app_addr[19:6],6'd05}] <= wr_data[ 40+:8];
          if (~wr_mask[ 6]) mem[{i_app_addr[19:6],6'd06}] <= wr_data[ 48+:8];
          if (~wr_mask[ 7]) mem[{i_app_addr[19:6],6'd07}] <= wr_data[ 56+:8];
          if (~wr_mask[ 8]) mem[{i_app_addr[19:6],6'd08}] <= wr_data[ 64+:8];
          if (~wr_mask[ 9]) mem[{i_app_addr[19:6],6'd09}] <= wr_data[ 72+:8];
          if (~wr_mask[10]) mem[{i_app_addr[19:6],6'd10}] <= wr_data[ 80+:8];
          if (~wr_mask[11]) mem[{i_app_addr[19:6],6'd11}] <= wr_data[ 88+:8];
          if (~wr_mask[12]) mem[{i_app_addr[19:6],6'd12}] <= wr_data[ 96+:8];
          if (~wr_mask[13]) mem[{i_app_addr[19:6],6'd13}] <= wr_data[104+:8];
          if (~wr_mask[14]) mem[{i_app_addr[19:6],6'd14}] <= wr_data[112+:8];
          if (~wr_mask[15]) mem[{i_app_addr[19:6],6'd15}] <= wr_data[120+:8];
          if (~wr_mask[16]) mem[{i_app_addr[19:6],6'd16}] <= wr_data[128+:8];
          if (~wr_mask[17]) mem[{i_app_addr[19:6],6'd17}] <= wr_data[136+:8];
          if (~wr_mask[18]) mem[{i_app_addr[19:6],6'd18}] <= wr_data[144+:8];
          if (~wr_mask[19]) mem[{i_app_addr[19:6],6'd19}] <= wr_data[152+:8];
          if (~wr_mask[20]) mem[{i_app_addr[19:6],6'd20}] <= wr_data[160+:8];
          if (~wr_mask[21]) mem[{i_app_addr[19:6],6'd21}] <= wr_data[168+:8];
          if (~wr_mask[22]) mem[{i_app_addr[19:6],6'd22}] <= wr_data[176+:8];
          if (~wr_mask[23]) mem[{i_app_addr[19:6],6'd23}] <= wr_data[184+:8];
          if (~wr_mask[24]) mem[{i_app_addr[19:6],6'd24}] <= wr_data[192+:8];
          if (~wr_mask[25]) mem[{i_app_addr[19:6],6'd25}] <= wr_data[200+:8];
          if (~wr_mask[26]) mem[{i_app_addr[19:6],6'd26}] <= wr_data[208+:8];
          if (~wr_mask[27]) mem[{i_app_addr[19:6],6'd27}] <= wr_data[216+:8];
          if (~wr_mask[28]) mem[{i_app_addr[19:6],6'd28}] <= wr_data[224+:8];
          if (~wr_mask[29]) mem[{i_app_addr[19:6],6'd29}] <= wr_data[232+:8];
          if (~wr_mask[30]) mem[{i_app_addr[19:6],6'd30}] <= wr_data[240+:8];
          if (~wr_mask[31]) mem[{i_app_addr[19:6],6'd31}] <= wr_data[248+:8];
          if (~wr_mask[32]) mem[{i_app_addr[19:6],6'd32}] <= wr_data[256+:8];
          if (~wr_mask[33]) mem[{i_app_addr[19:6],6'd33}] <= wr_data[264+:8];
          if (~wr_mask[34]) mem[{i_app_addr[19:6],6'd34}] <= wr_data[272+:8];
          if (~wr_mask[35]) mem[{i_app_addr[19:6],6'd35}] <= wr_data[280+:8];
          if (~wr_mask[36]) mem[{i_app_addr[19:6],6'd36}] <= wr_data[288+:8];
          if (~wr_mask[37]) mem[{i_app_addr[19:6],6'd37}] <= wr_data[296+:8];
          if (~wr_mask[38]) mem[{i_app_addr[19:6],6'd38}] <= wr_data[304+:8];
          if (~wr_mask[39]) mem[{i_app_addr[19:6],6'd39}] <= wr_data[312+:8];
          if (~wr_mask[40]) mem[{i_app_addr[19:6],6'd40}] <= wr_data[320+:8];
          if (~wr_mask[41]) mem[{i_app_addr[19:6],6'd41}] <= wr_data[328+:8];
          if (~wr_mask[42]) mem[{i_app_addr[19:6],6'd42}] <= wr_data[336+:8];
          if (~wr_mask[43]) mem[{i_app_addr[19:6],6'd43}] <= wr_data[344+:8];
          if (~wr_mask[44]) mem[{i_app_addr[19:6],6'd44}] <= wr_data[352+:8];
          if (~wr_mask[45]) mem[{i_app_addr[19:6],6'd45}] <= wr_data[360+:8];
          if (~wr_mask[46]) mem[{i_app_addr[19:6],6'd46}] <= wr_data[368+:8];
          if (~wr_mask[47]) mem[{i_app_addr[19:6],6'd47}] <= wr_data[376+:8];
          if (~wr_mask[48]) mem[{i_app_addr[19:6],6'd48}] <= wr_data[384+:8];
          if (~wr_mask[49]) mem[{i_app_addr[19:6],6'd49}] <= wr_data[392+:8];
          if (~wr_mask[50]) mem[{i_app_addr[19:6],6'd50}] <= wr_data[400+:8];
          if (~wr_mask[51]) mem[{i_app_addr[19:6],6'd51}] <= wr_data[408+:8];
          if (~wr_mask[52]) mem[{i_app_addr[19:6],6'd52}] <= wr_data[416+:8];
          if (~wr_mask[53]) mem[{i_app_addr[19:6],6'd53}] <= wr_data[424+:8];
          if (~wr_mask[54]) mem[{i_app_addr[19:6],6'd54}] <= wr_data[432+:8];
          if (~wr_mask[55]) mem[{i_app_addr[19:6],6'd55}] <= wr_data[440+:8];
          if (~wr_mask[56]) mem[{i_app_addr[19:6],6'd56}] <= wr_data[448+:8];
          if (~wr_mask[57]) mem[{i_app_addr[19:6],6'd57}] <= wr_data[456+:8];
          if (~wr_mask[58]) mem[{i_app_addr[19:6],6'd58}] <= wr_data[464+:8];
          if (~wr_mask[59]) mem[{i_app_addr[19:6],6'd59}] <= wr_data[472+:8];
          if (~wr_mask[60]) mem[{i_app_addr[19:6],6'd60}] <= wr_data[480+:8];
          if (~wr_mask[61]) mem[{i_app_addr[19:6],6'd61}] <= wr_data[488+:8];
          if (~wr_mask[62]) mem[{i_app_addr[19:6],6'd62}] <= wr_data[496+:8];
          if (~wr_mask[63]) mem[{i_app_addr[19:6],6'd63}] <= wr_data[504+:8];
          wdf_rdy <= 1'b1;
        end
      end
    end else begin
      rd_data_valid <= 1'b0;
      if (i_app_wdf_wren) begin
        wr_data <= i_app_wdf_data;
        wr_mask <= i_app_wdf_mask;
        wdf_rdy <= 1'b0;
      end
    end
  end
end

endmodule
